package perso.marcr.lst.spring3.controller;

import java.io.IOException;
import java.text.ParseException;
import java.util.Collections;
import java.util.List;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.oxm.XmlMappingException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.xml.sax.SAXException;

import perso.marcr.lst.model.Configuration;
import perso.marcr.lst.model.DBModel;
import perso.marcr.lst.model.TaskList;
import perso.marcr.lst.service.ConfigurationService;
import perso.marcr.lst.service.CronService;
import perso.marcr.lst.service.JobService;
import perso.marcr.lst.service.TaskService;
import perso.marcr.util.Unit;


@Controller
@SessionAttributes
@RequestMapping("/job")
public class JobController extends ParentController {

	public JobController() {
		super(JobController.class);
	}

	@Autowired
	private ConfigurationService configurationService = null;

	@Autowired
	private CronService cronService = null;

	@Autowired
	private JobService jobService = null;

	@Autowired
	private TaskService taskService = null;


	private static final String URI_DASHBOARD = "redirect:/job/dashboard/all.html";

	/**
	 * @param model
	 * @return
	 */
	@RequestMapping(value="/dashboard/all")
	protected String dashboardAll(Model model)
	{
		List<String> jobs = jobService.getAllJobs();		
		model.addAttribute("results", jobs);
		return "/job/dashboard/all";
	}

	/**
	 * @param model
	 * @return
	 */
	@RequestMapping(value="/dashboard/cronTaskList", method = RequestMethod.GET)
	protected void dashboardCronTaskList(Model model)
	{
		try {
			model.addAttribute("list", cronService.getCurrentlyExecutingJobs());
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	/**
	 * @param jobName
	 * @param model
	 * @return
	 */
	@RequestMapping(value="/dashboard/cronPendingList", method = RequestMethod.GET)
	protected void dashboardCronPendingList(Model model)
	{
		try {
			model.addAttribute("list", cronService.getPendingJobs());
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @param jobName
	 * @param model
	 * @return
	 */
	@RequestMapping(value="/view/index-{jobName}", method = RequestMethod.GET)
	protected String viewIndex(@PathVariable String jobName, Model model)
	{
		model.addAttribute("jobName", jobName);
		try {
			Configuration configuration = configurationService.load(jobName);
			model.addAttribute("description", configuration.getDescription());
		} catch (ParserConfigurationException e) {
			addError(e, model);
		} catch (SAXException e) {
			addError(e, model);
		} catch (IOException e) {
			addError(e, model);
		} catch (JAXBException e) {
			addError(e, model);
		}
		return "/job/view/index";
	}


	/**
	 * @param model
	 * @param jobName
	 * @param jobHostDir
	 * @return
	 */
	@RequestMapping(value="/view/create", method = RequestMethod.GET)
	protected String viewCreate(RedirectAttributes model, @RequestParam String jobName, @RequestParam String jobHostDir)
	{
		String szReturn = URI_DASHBOARD;
		try {
			if(isJobValid(jobName, model))
			{
				jobService.create(jobName);
				configurationService.create(jobName, jobHostDir);
				szReturn = "redirect:/job/view/index-"+jobName+".html";
			}
		} catch (ParserConfigurationException e) {
			addError(e, model);
		} catch (TransformerException e) {
			addError(e, model);
		} catch (XmlMappingException e) {
			addError(e, model);
		} catch (JAXBException e) {
			addError(e, model);
		} catch (IOException e) {
			addError(e, model);
		}

		return szReturn;
	}

	@RequestMapping(value="/view/configuration-{jobName}", method = RequestMethod.GET)
	protected String viewConfiguation(@PathVariable String jobName, Model model, RedirectAttributes rmodel)
	{
		DBModel dbModel = new Configuration();
		String szReturn = URI_DASHBOARD;
		try
		{	
			if(isJobNameWellFormed(jobName, rmodel))
			{
				dbModel = configurationService.load(jobName);
				model.addAttribute("results", dbModel);
				model.addAttribute("units", Unit.values());
				szReturn = "/job/view/configuration";
			}
		}
		catch (ParserConfigurationException e) {			 
			addError(e, rmodel);
		} catch (SAXException e) {			
			addError(e, rmodel);
		} catch (IOException e) {
			addError(e, rmodel);
		} catch (JAXBException e) {
			addError(e, rmodel);
		}

		return szReturn;
	}

	@RequestMapping(value="/view/updateConfiguration", method = RequestMethod.POST)
	protected String viewUpdateConfiguration(RedirectAttributes model, Configuration configuration, String jobId)
	{
		String szReturn = URI_DASHBOARD;
		try {
			cronService.removeFromCron(jobId);
			String jobName=jobId;

			if(!jobId.equals(configuration.getJobName())) {
				// user asked to rename the job:
				if(isJobValid(configuration.getJobName(), model))
				{
					jobService.renameJob(jobId, configuration.getJobName());
					jobName = configuration.getJobName();
				}
			}

			// save job (if the jobName is invalid, it will display an error message, but the configuration is saved!)
			configurationService.save(jobName, configuration);
			szReturn = "redirect:/job/view/index-"+jobName+".html";
			cronService.updateCron(jobName, configuration.getCronExpression());

		} catch (TransformerConfigurationException e) {
			addError(e, model);
		} catch (ParserConfigurationException e) {
			addError(e, model);
		} catch (TransformerException e) {
			addError(e, model);
		} catch (IOException e) {
			addError(e, model);
		} catch (XmlMappingException e) {
			addError(e, model);
		} catch (JAXBException e) {
			addError(e, model);
		} catch (SchedulerException e) {
			addError(e, model);
		} catch (ParseException e) {
			addError(e, model);
		}
		return szReturn;
	}

	@RequestMapping(value="/view/delete-{jobName}", method = RequestMethod.GET)
	protected String viewDelete(RedirectAttributes model, @PathVariable String jobName)
	{
		try {
			jobService.delete(jobName);
		} catch (IOException e) {
			addError(e, model);
		}
		return URI_DASHBOARD;
	}

	@RequestMapping(value="/view/askTaskList-{jobName}", method = RequestMethod.GET)
	protected void viewAskTaskList(Model model, @PathVariable String jobName)
	{
		;
		try {
			List<TaskList> listTask = taskService.getAllTasks(jobName);
			Collections.sort(listTask, TaskList.TaskListaComparator);
			model.addAttribute("list", listTask);
		} catch (JAXBException e) {
			addError(e, model);
		}
	}

	@RequestMapping(value="/view/task-{jobName}-{taskId}", method = RequestMethod.GET)
	protected String viewTask(Model model, @PathVariable String jobName, @PathVariable String taskId)
	{
		model.addAttribute("jobName", jobName);
		model.addAttribute("taskId", taskId);
		try {
			Configuration configuration = configurationService.load(jobName);
			model.addAttribute("description", configuration.getDescription());
		} catch (ParserConfigurationException e) {
			addError(e, model);
		} catch (SAXException e) {
			addError(e, model);
		} catch (IOException e) {
			addError(e, model);
		} catch (JAXBException e) {
			addError(e, model);
		}
		return "/job/view/task";
	}

	private boolean isJobNameWellFormed(String jobName, RedirectAttributes model)
	{
		boolean bIsJobNameWellFormed = true;
		if(!jobService.jobNameWellFormed(jobName))
		{
			String szErrorMessage = String.format("Job %s must only contains characters: %s", jobName, JobService.JOB_NAME_REGEXPR);
			addError(szErrorMessage, model);
			bIsJobNameWellFormed = false;
		}
		return bIsJobNameWellFormed;
	}

	private boolean isJobExists(String jobName, RedirectAttributes model)
	{
		boolean bIsJobExists = false;
		if(jobService.jobExists(jobName))
		{
			String szErrorMessage = String.format("Job %s already exists", jobName);
			addError(szErrorMessage, model);
			bIsJobExists = true;
		}
		return bIsJobExists;
	}

	/**
	 * @param jobName
	 * @param model
	 * @return true if job is valid, else false.
	 */
	private boolean isJobValid(String jobName, RedirectAttributes model)
	{		
		return isJobNameWellFormed(jobName, model) && !isJobExists(jobName, model);
	}
}
